/*
 * @Author: your name
 * @Date: 2020-12-26 22:42:35
 * @LastEditTime: 2020-12-27 14:33:25
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \learn-fe\handWrite\myPromise\myPromise.ts
 */

type Fun = (arg0: any) => void;

class MyPromise {
  static all;
  static any;
  static race;
  static resolve;
  static reject;
  static finally;
  private state: string;
  private onRejectedFns: Fun[];
  private onResolvedFns: Fun[];
  private value: any;
  private reason: any;
  constructor(excute) {
    if (typeof excute !== "function") {
      throw new Error("参数错误");
    }
    this.state = "";
    this.onRejectedFns = [];
    this.onResolvedFns = [];

    const resolve = function (value: any) {
      setTimeout(() => {
        if (this.state === "pending") {
          this.state = "fulfilled";
          this.value = value;
          this.onResolvedFns.forEach((fn) => {
            fn(this.value);
          });
        }
      });
    };

    const reject = function (reason: any) {
      setTimeout(() => {
        if (this.state === "pending") {
          this.state = "rejected";
          this.reason = reason;
        }
        this.onRejectedFns.forEach((fn) => {
          fn(this.value);
        });
      });
    };

    try {
      excute(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  resolvePromise(x, promise, resolve, reject) {
    if (x === promise) {
      throw new Error("type Error");
    }
    if (x !== null && (typeof x === "object" || typeof x === "function")) {
      // 判断是否执行过
      let excuted = false;
      let then = x.then;
      try {
        if (typeof then === "function") {
          then.call(
            x,
            (y) => {
              if (!excuted) {
                excuted = true;
                resolve(y);
              }
            },
            (err) => {
              if (!excuted) {
                excuted = true;
                reject(err);
              }
            }
          );
        } else {
          resolve(x);
        }
      } catch (e) {
        reject(e);
      }
    } else {
      resolve(x);
    }
  }
  then(onResolve, onRejct) {
    let x;
    let promise;
    // 为了实现链式调用，继续返回MyPromise实例
    onResolve =
      typeof onResolve === "function"
        ? onResolve
        : (x) => {
            return x;
          };
    onRejct =
      typeof onRejct === "function"
        ? onRejct
        : (err) => {
            throw new Error(err);
          };

    promise = new MyPromise((resolve, reject) => {
      if (this.state === "fulfilled") {
        setTimeout(() => {
          try {
            x = onResolve(this.value);
            this.resolvePromise(x, promise, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
      if (this.state === "rejected") {
        setTimeout(() => {
          try {
            x = onRejct(this.reason);
            this.resolvePromise(x, promise, resolve, reject);
          } catch (e) {
            throw e;
          }
        });
      }
      if (this.state === "pending") {
        this.onResolvedFns.push(() => {
          try {
            x = onResolve(this.value);
            this.resolvePromise(x, promise, resolve, reject);
          } catch (e) {
            throw e;
          }
        });
        this.onRejectedFns.push(() => {
          try {
            x = onRejct(this.reason);
            this.resolvePromise(x, promise, resolve, reject);
          } catch (e) {
            throw e;
          }
        });
      }
    });
    return promise;
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }
}
// resolve方法
MyPromise.resolve = (value) => {
  if (value instanceof MyPromise) {
    return value;
  } else {
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }
};
// catch 方法
MyPromise.reject = (reason) => {
  return new MyPromise((resolve, reject) => {
    reject(reason);
  });
};
MyPromise.all = function (array) {
  return new MyPromise((resolve, reject) => {
    let result = [];
    if (!(array instanceof Array) || array.length === 0) {
      resolve([]);
    }

    try {
      for (let i = 0; i < array.length; i++) {
        const task = array[i];
        const val = MyPromise.resolve(task);
        result[i] = val;
      }
      resolve(result);
    } catch (reason) {
      reject(reason);
    }
  });
};
